<?php

namespace Gql\Tools;


use Gql\Contracts\TypeInterface;

class ResolveSchema
{
    public $dirPath;
    public $namespace;
    public $resolveSchema;

    public function __construct(?string $dirPath = null, ?string $namespace = null)
    {
        if (is_null($dirPath)) {
            $dirPath = __DIR__;
        }
        if (is_null($namespace)) {
            $namespace = __NAMESPACE__;
        }
        $this->dirPath = $dirPath;
        $this->namespace = $namespace;
    }

    /**
     * 获取类名（如果方法不存在则返回null）
     * @param string $typeName 类型名称
     * @param string $fieldName 方法名称
     * @return string|null
     * @throws \ReflectionException
     */
    public function getClassName(string $typeName, string $fieldName): ?string
    {

        $schema = $this->getResolveSchemas();
        if (isset($schema[$typeName])) {
            $typeNames = $schema[$typeName];
            return in_array($fieldName, $typeNames['methods']) ? $typeNames['className'] : null;
        }
        return null;
    }
    public function getClassName222(string $typeName, string $fieldName): ?string
    {
        $schema = $this->getResolveSchemas();
        if (isset($schema[$typeName])) {
            $typeNames = $schema[$typeName];
            return in_array($fieldName, $typeNames['methods']) ? $typeNames['className'] : null;
        }
        return null;
    }

    /**
     * 返回 ResolveSchema
     * @return array
     * @throws \ReflectionException
     */
    public function getResolveSchemas(): array
    {
        if (!$this->resolveSchema) {
            $this->buildResolveSchema();
        }
        return $this->resolveSchema;
    }

    /**
     * 设置Resolve类地址路径（所有的resolve类应该仅在同一个目录下）
     * @param string $path
     * @return  void
     */
    public function setDirPath(string $path): void
    {
        $this->dirPath = $path;
    }

    /**
     * 设置Resolve类命名空间（所有的resolve类应该仅有唯一的命名空间）
     * @param string $namespace
     * @return  void
     */
    public function setNamespace(string $namespace): void
    {
        $this->namespace = $namespace;
    }

    /**
     * 生成 ResolveSchema
     * @throws \ReflectionException
     */
    protected function buildResolveSchema(): void
    {
        $this->resolveSchema = $this->parseResolves($this->dirPath, $this->namespace);
    }

    /**
     * 通过类目录路径和命名空间解析类型生成
     * @param $dir
     * @param $namespace
     * @return array
     * @throws \ReflectionException
     */
    protected function parseResolves($dir, $namespace): array
    {
        $resolves = []; //类名、方法名数组
        if (is_dir($dir)) {

            if ($dh = opendir($dir)) {
                while (($file = readdir($dh)) !== false) {
                    if ($file === '.' || $file === '..') {
                        continue;
                    }
                    $fileInfo = pathinfo($file);
                    if (!isset($fileInfo['extension']) || $fileInfo['extension'] !== 'php') {
                        continue;
                    }
                    $className = $namespace . $fileInfo['filename'];
                    if (class_exists($className)) {

                        $item = $this->parseMethodsByClass($className);


                        if (is_null($item) || empty($item)) continue;
                        $resolves[$fileInfo['filename']] = [
                            'className' => $className,
                            'methods' => $item
                        ];
                    }
                }
                closedir($dh);
            }
        }
        return $resolves;
    }

    /**
     * 解析类并返回实现了TypeInterface的类的公共的方法组
     * @param $className
     * @return array|null
     * @throws \ReflectionException
     */
    protected function parseMethodsByClass($className): ?array
    {

        $resolve = \App::make($className);
        if (!$resolve instanceof TypeInterface) {
            return null;
        }
        $class = new \ReflectionClass($resolve);
        $methods = $class->getMethods();
        $item = []; //类方法名组
        foreach ($methods as $key => $v) {
            if ($className === $v->class && \ReflectionMethod::IS_PUBLIC === $v->getModifiers()) {
                $item[] = $v->name;
            }

        }
        return $item;
    }
}